<!--
 * @Author: WuFeng <763467339@qq.com>
 * @Date: 2024-11-13 15:15:57
 * @LastEditTime: 2024-11-19 13:32:40
 * @LastEditors: WuFeng <763467339@qq.com>
 * @Description: 
 * @FilePath: \markdown-magic\src\pages\Pdf\page\entry\pdfToWord.vue
 * Copyright 版权声明
-->
<template>
  <div class="pageWrap">
    <a-spin :tip="spinningConfig.tip" :spinning="spinningConfig.spinning">
      <div class="descArea">
        在线批量将PDF转为doc/docx文件
      </div>
      <a-upload-dragger
        v-show="fileList.length === 0"
        v-model:fileList="fileList"
        accept=".pdf,.PDF"
        name="file"
        :maxCount="1"
        :multiple="false"
        :showUploadList="false"
        action=""
        :before-upload="beforeUpload"
        :customRequest="customRequest"
        list-type="picture"
        @change="handleChange"
        @drop="handleDrop"
        @remove="handleRemove"
      >
        <p class="ant-upload-drag-icon">
          <inbox-outlined></inbox-outlined>
        </p>
        <p class="ant-upload-text">点击选择或拖拽文件到这里上传</p>
        <p class="ant-upload-hint">
          可上传1个PDF文件，单个不超过100MB（拖拽调整顺序）
        </p>
      </a-upload-dragger>
      <div class="dragWrap" v-show="fileList.length > 0">
        <div class="dragHeader">
          <div class="label name">
            名称
          </div>
          <div class="label page">
            页数
          </div>
          <!-- <div class="label range">
            输出范围
          </div> -->
          <div class="label status">
            状态
          </div>
          <div class="label handle">
            操作
          </div>
        </div>
        <draggable
          class="dragBody"
          :list="fileList"
          item-key="uid"
          ghost-class="ghost"
          chosen-class="chosen"
          @start="handleDragStart"
          @end="handleDragEnd"
          animation="300"
        >
          <template #item="{ element, index }">
            <div
              class="item"
              :class="{ 'not-draggable': !draggableState.enabled }"
              :key="element.id"
            >
              <div class="label name">
                <strong>
                  {{ element.name }}
                </strong>
              </div>
              <div class="label page">
                {{ element.pageCount }}
              </div>
              <!-- <div class="label range">
                <a-input-number 
                  v-model:value="element.processConfig.pageRange.start" 
                  @change="(val) => handleChangeRange(val, index, 'start')"
                  size="small" 
                  :min="1" 
                  :max="element?.pageCount" />
                <span class="line">
                  -
                </span>
                <a-input-number 
                  v-model:value="element.processConfig.pageRange.end" 
                  @change="(val) => handleChangeRange(val, index, 'end')"
                  size="small" 
                  :min="1" 
                  :max="element?.pageCount" />
              </div> -->
              <div class="label status">
                <a-tag color="orange" v-if="element.processConfig.status === '未转换'">未转换</a-tag>
                <a-tag color="orange" v-if="element.processConfig.status === '转换中'">转换中</a-tag>
                <a-tag color="red" v-if="element.processConfig.status === '转换异常'">转换异常</a-tag>
                <a-tag color="green" v-if="element.processConfig.status === '转换完成'">转换完成</a-tag>
              </div>
              <div class="label handle">
                <DeleteOutlined 
                  style="color: orangered;"
                  @click="handleDelete(element, index)" />
              </div>
            </div>
          </template>
        </draggable>
      </div>
      <div class="btnsWrap">
        <a-space>
          <a-button v-if="fileList.length > 0" type="primary" @click="handleProcess"><PlayCircleOutlined />开始转换</a-button>
          <a-button v-if="fileList.length > 0" danger @click="handleClear"><ClearOutlined />清空文件</a-button>
          <a-button v-if="processResult !== null" type="primary" ghost @click="handleDownload"><DownloadOutlined />下载</a-button>
        </a-space>
      </div>
    </a-spin>
  </div>
</template>

<script setup>
import { ref, reactive, defineEmits, computed } from 'vue'
import { message } from 'ant-design-vue'
import {
  PlayCircleOutlined,
  FilePdfOutlined,
  DownloadOutlined,
  ClearOutlined,
  DeleteOutlined
} from '@ant-design/icons-vue'
import { PDFDocument } from 'pdf-lib'

import PdfPreview from '../../components/PdfPreview.vue'

import draggable from 'vuedraggable'

import { pdfToWord } from '@/api/pdf.js'
import { resolveBlob } from '@/utils/fs'

const emit = defineEmits([])

// loading 配置
const spinningConfig = reactive({
  spinning: false,
  tip: '处理中...'
})

const fileList = ref([])

const processResult = ref(null)

const handleChangeRange = (val, index, type) => {
  if (val === undefined || val === null || val === '') {
    if (type === 'start') {
      fileList.value[index].processConfig.pageRange.start = 1
    } else {
      fileList.value[index].processConfig.pageRange.end = fileList.value[index].pageCount
    }
  }
}

const draggableState = reactive({
  enabled: true,
  dragging: false,
})

const handleDragStart = () => {
  draggableState.dragging = true
}

const handleDragEnd = () => {
  draggableState.dragging = false
}

const handleDelete = (element, index) => {
  const { uid } = element
  fileList.value = fileList.value.filter(item => item.uid!== uid)
  message.success(`删除文件成功`)
}

const handleRemove = (file) => {
  return false
}

const handleChange = (info) => {
  const status = info.file.status
  if (status === 'uploading') {
  } else if (status === 'done') {
    message.success(`${info.file.name} 文件上传成功.`)
  } else if (status === 'error') {
    message.error(`${info.file.name} 文件上传错误.`)
  } else if (status === 'removed') {
    message.error(`${info.file.name} 文件删除成功.`)
  }
}
const handleDrop = (e) => {
  console.log(e)
}

const beforeUpload = (file) => {
  const isJpgOrPng = file.type === 'application/pdf';
  if (!isJpgOrPng) {
    message.error('请上传pdf格式的文件!');
  }
  const isLt2M = file.size / 1024 / 1024 < 1000;
  if (!isLt2M) {
    message.error('文件必须小于1000MB!');
  }
  file.processConfig = {
    pageRange: {
      start: 1,
      end: 1
    },
    status: '未转换'
  }
  return isJpgOrPng && isLt2M;
}

// 自定义上传
const customRequest = async ({ action, file, onSuccess, onError }) => {
  spinningConfig.spinning = true
  spinningConfig.tip = '上传中...'
  try {
    fileList.value.find((item, index) => {
      if (item.uid === file.uid) {
        item.status = 'done'
        
        // 读取文件
        const reader = new FileReader();
        reader.onload = async function (e) {
          // 你可以在这里处理二进制内容
          const arrayBuffer = this.result;
    
          item.arrayBuffer = arrayBuffer
    
          let existingPdf = null
          try {
            // 加载现有的 PDF 文档
            existingPdf = await PDFDocument.load(arrayBuffer);
          } catch (error) {
            message.error(`文件【${item.name}】有密码保护，无法解析，无法合并！`)
            // 关闭loading
            spinningConfig.spinning = false
          }
          if (existingPdf === null) {
            fileList.value.splice(index, 1)
            return
          }
    
          // 获取所有页面
          item.existingPdf = existingPdf
          item.pages = existingPdf.getPages();
          item.pageCount = existingPdf.getPageCount();
  
          // 初始化配置项
          item.processConfig = {
            pageRange: {
              start: 1,
              end: item.pageCount
            },
            status: '未转换'
          }
  
          // 关闭loading
          spinningConfig.spinning = false
  
        }
    
        reader.readAsArrayBuffer(item.originFileObj);
      }
      return item.uid === file.uid
    })
  } catch (error) {
    // 关闭loading
    spinningConfig.spinning = false
    message.error(error)
  }
}

// 开始转换
const handleProcess = async () => {
  if (fileList.value.length === 0) {
    message.warning('请上传至少一个pdf文件!')
    return
  }

  spinningConfig.spinning = true
  spinningConfig.tip = '转换中...请耐心等待...'

  fileList.value[0].processConfig.status = '转换中'

  const file = fileList.value[0].originFileObj
  const formData = new FormData()
  formData.append('file', file)

  try {
    const result = await pdfToWord(formData)
    processResult.value = result
    fileList.value[0].processConfig.status = '转换完成'
  } catch (error) {
    fileList.value[0].processConfig.status = '转换异常'
  } finally {
    // 关闭loading
    spinningConfig.spinning = false
  }

}

// 清空文件
const handleClear = () => {
  fileList.value = []
  processResult.value = null
  message.success(`清空文件成功`)
}

// 下载文件
const handleDownload = async () => {
  resolveBlob(processResult.value, 'application/pdf')
}
</script>

<style lang="scss" scoped>
.pageWrap {
  :deep(.ant-upload-wrapper .ant-upload-drag){
    height: auto !important;
  }
  .descArea{
    line-height: 1.6em;
    font-size: 14px;
    color: #666;
    margin-bottom: 12px;
  }
  .dragWrap{
    padding-top: 12px;
    .label{
      padding: 12px;
    }
    .name{
      flex: 1;
    }
    .page{
      width: 80px;
      text-align: center;
    }
    .range{
      width: 160px;
      display: flex;
      align-items: center;
      justify-content: center;
      .line{
        margin: 0 5px;
      }
    }
    .status{
      width: 80px;
      text-align: center;
    }
    .handle{
      width: 120px;
      text-align: right;
    }
    .dragHeader{
      display: flex;
      align-items: center;
      margin-bottom: 3px;
      border: 1px solid #eee;
      background-color: #f0f0f0;
      .label{
        font-size: 14px;
        color: #333;
        font-weight: bold;
      }
    }
    .dragBody{
      .item {
        margin-bottom: 3px;
        cursor: move;
        background-color: #f9f9f9;
        border: 1px solid #eee;
        display: flex;
        &:hover {
          background-color: #f0f0f0;
        }
        .name{
          strong{
            font-weight: normal;
            font-size: 14px;
          }
        }

        .handle{
          cursor: pointer;
        }
      }
      .not-draggable {
        cursor: no-drop;
      }
      .ghost {
        opacity: 0.5;
        border: 1px solid #18a058;
      }
      .chosen {
        border: 1px solid #18a058;
      }
    }
  }
  .btnsWrap{
    margin: 16px 0;
  }
}
</style>